#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "gc.h"
#include "mem.h"
#include "thread.h"
#include "redintobject.h"
#include "redboolobject.h"
#include "rednullobject.h"
#include "redarrayobject.h"
#include "redtupleobject.h"
#include "redstringobject.h"

#define RED_ARRAY_INIT_PRESIZE		((void *)(-1))

static RedObject *array_len(RedObject *self)
{
	return RedInt_FromInt(RedArray_Length(self));
}

static RedObject *array_repr(RedObject *self)
{
	int nmods;
	RedArray_ReadLock(self, nmods);

	if (RedArray_Length(self) == 0)
	{
		RedArray_Unlock(self);
		return RedString_FromCString("[]");
	}

	if (!RedObject_ReprEnter(self))
	{
		RedArray_Unlock(self);
		return RedString_FromCString("...");
	}

	RedObject *result = RedString_FromChar('[');

	if (!result)
	{
		RedObject_ReprLeave(self);
		RedArray_Unlock(self);
		return NULL;
	}

	for (int i = 0; i < RedArray_Length(self); i++)
	{
		RedObject *item = RedObject_Repr(RedArray_Items(self)[i]);

		if (!item)
		{
			Red_UNREF(result);
			RedObject_ReprLeave(self);
			RedArray_Unlock(self);
			return NULL;
		}

		if (!RedArray_CheckMods(self, nmods))
		{
			Red_UNREF(item);
			Red_UNREF(result);
			RedObject_ReprLeave(self);
			RedArray_Unlock(self);
			return NULL;
		}

		RedObject *newstring = RedString_Append(result, item);

		Red_UNREF(item);
		Red_UNREF(result);

		if (!newstring)
		{
			RedObject_ReprLeave(self);
			RedArray_Unlock(self);
			return NULL;
		}

		if (i == RedArray_Length(self) - 1)
			result = RedString_AppendCString(newstring, "]");
		else
			result = RedString_AppendCString(newstring, ", ");

		Red_UNREF(newstring);
	}

	RedObject_ReprLeave(self);
	RedArray_Unlock(self);
	return result;
}

static char array_init(RedObject *self, RedObject *args)
{
	RedMutex_Init(RedArray_Mutex(self));

	if (!Red_INIT())
		return 0;

	/* args == NULL ---> internal object */
	if (!args)
	{
		if (!(RedArray_Items(self) = RedMem_Alloc(sizeof(RedObject *) * RED_ARRAY_INIT_SIZE)))
			return 0;

		RedArray_NMods(self) = 0;
		RedArray_Length(self) = 0;
		RedArray_Capacity(self) = RED_ARRAY_INIT_SIZE;
		memset(RedArray_Items(self), 0, sizeof(RedObject *) * RED_ARRAY_INIT_SIZE);
		return 1;
	}

	/* initialize presized */
	if (args == RED_ARRAY_INIT_PRESIZE)
		return 1;

	if (RedArray_Exact(args))
	{
		if (!(RedArray_Items(self) = RedMem_Alloc(sizeof(RedObject *) * RedArray_Capacity(args))))
			return 0;

		RedArray_NMods(self) = 0;
		RedArray_Length(self) = RedArray_Length(args);
		RedArray_Capacity(self) = RedArray_Capacity(args);

		for (int i = 0; i < RedArray_Length(args); i++)
		{
			if (RedObject_GC_IsTracked(self))
				RedObject_GC_Insert(RedArray_Items(args)[i]);

			Red_REF(RedArray_Items(args)[i]);
			RedArray_Items(self)[i] = RedArray_Items(args)[i];
		}

		return 1;
	}

	if (RedTuple_Exact(args))
	{
		if (!(RedArray_Items(self) = RedMem_Alloc(sizeof(RedObject *) * RedTuple_Length(args))))
			return 0;

		RedArray_NMods(self) = 0;
		RedArray_Length(self) = RedTuple_Length(args);
		RedArray_Capacity(self) = RedTuple_Length(args);

		for (int i = 0; i < RedTuple_Length(args); i++)
		{
			if (RedObject_GC_IsTracked(self))
				RedObject_GC_Insert(RedTuple_Items(args)[i]);

			Red_REF(RedTuple_Items(args)[i]);
			RedArray_Items(self)[i] = RedTuple_Items(args)[i];
		}

		return 1;
	}

	RedException_ThrowFormat(&RedExc_ValueError, "'array' or 'tuple' expected");
	return 0;
}

static void array_clear(RedObject *self)
{
	for (int i = 0; i < RedArray_Length(self); i++)
		Red_UNREF(RedArray_Items(self)[i]);
}

static void array_release(RedObject *self)
{
	array_clear(self);
	RedMutex_Free(RedArray_Mutex(self));
	RedMem_Free(RedArray_Items(self));
	Red_RELEASE();
}

static void array_traverse(RedObject *self, tp_visit_fn_t visit, void *args)
{
	for (int i = 0; i < RedArray_Length(self); i++)
		Red_VISIT(RedArray_Items(self)[i]);
}

static RedObject *array_aug_add(RedObject *self, RedObject *other)
{
	if (!RedArray_Extend(self, other))
		return NULL;
	else
		Red_Return_Null();
}

static RedObject *array_sort(RedObject *self, RedObject *args)
{
	RedObject *cmp;

	if (!RedTuple_Unpack(args, "C", &cmp))
		return NULL;

	if (!RedArray_Sort(self, cmp))
		return NULL;
	else
		Red_Return_Null();
}

static RedObject *array_clear_(RedObject *self, RedObject *args)
{
	if (!RedTuple_Unpack(args, ""))
		return NULL;

	if (!RedArray_Clear(self))
		return NULL;
	else
		Red_Return_Null();
}

static RedObject *array_append(RedObject *self, RedObject *args)
{
	RedObject *item;

	if (!RedTuple_Unpack(args, "?", &item))
		return NULL;

	if (!RedArray_Append(self, item))
		return NULL;
	else
		Red_Return_Null();
}

static RedObject *array_remove(RedObject *self, RedObject *args)
{
	RedObject *item;

	if (!RedTuple_Unpack(args, "?", &item))
		return NULL;

	if (!RedArray_Remove(self, item))
		return NULL;
	else
		Red_Return_Null();
}

static RedObject *array_extend(RedObject *self, RedObject *args)
{
	RedObject *array;

	if (!RedTuple_Unpack(args, "a", &array))
		return 0;

	if (!RedArray_Extend(self, array))
		return NULL;
	else
		Red_Return_Null();
}

static RedObject *array_insert(RedObject *self, RedObject *args)
{
	int64_t index;
	RedObject *item;

	if (!RedTuple_Unpack(args, "?i*", &item, &index))
		return 0;

	if (!RedArray_Insert(self, item, (int)index))
		return NULL;
	else
		Red_Return_Null();
}

static RedObject *array_pop(RedObject *self, RedObject *args)
{
	return RedTuple_Unpack(args, "") ? RedArray_Pop(self) : NULL;
}

static RedObject *array_copy(RedObject *self, RedObject *args)
{
	return RedTuple_Unpack(args, "") ? RedArray_Copy(self) : NULL;
}

static RedObject *array_foreach(RedObject *self, RedObject *args)
{
	RedObject *callable;

	if (!RedTuple_Unpack(args, "C", &callable))
		return NULL;

	RedObject *result = RedArray_CreatePresized(RedArray_Length(self));

	if (!result)
		return NULL;

	for (int i = 0; i < RedArray_Length(self); i++)
		RedArray_Items(result)[i] = NULL;

	int nmods;
	RedArray_ReadLock(self, nmods);

	for (int i = 0; i < RedArray_Length(self); i++)
	{
		RedObject *n = RedInt_FromInt(i);
		RedObject *arg = RedArray_Items(self)[i];
		RedObject *tuple = RedTuple_FromObjects(2, n, arg);
		RedArray_Items(result)[i] = RedObject_Invoke(callable, tuple);

		Red_UNREF(n);
		Red_UNREF(tuple);

		if (!(RedArray_Items(result)[i]))
		{
			Red_UNREF(result);
			RedArray_Unlock(self);
			return NULL;
		}

		if (!RedArray_CheckMods(self, nmods))
		{
			Red_UNREF(result);
			return NULL;
		}
	}

	RedArray_Unlock(self);
	return result;
}

static RedObject *array_contains(RedObject *self, RedObject *args)
{
	RedObject *value;

	if (!RedTuple_Unpack(args, "?", &value))
		return NULL;

	int nmods;
	RedArray_ReadLock(self, nmods);

	for (int i = 0; i < RedArray_Length(self); i++)
	{
		RedObject *item = RedArray_Items(self)[i];
		RedObject *equals = RedObject_Eq(value, item);

		if (!equals)
		{
			RedArray_Unlock(self);
			return NULL;
		}

		if (RedBool_IsTrue(equals))
		{
			Red_UNREF(equals);
			RedArray_Unlock(self);
			Red_Return_True();
		}

		if (!RedArray_CheckMods(self, nmods))
		{
			Red_UNREF(equals);
			return NULL;
		}

		Red_UNREF(equals);
	}

	RedArray_Unlock(self);
	Red_Return_False();
}

static RedMethodDesc array_methods[] =
{
	{ "sort"	, array_sort		},
	{ "clear"	, array_clear_		},
	{ "append"	, array_append		},
	{ "remove"	, array_remove		},
	{ "extend"	, array_extend		},
	{ "insert"	, array_insert		},

	{ "pop"		, array_pop			},
	{ "copy"	, array_copy		},
	{ "foreach"	, array_foreach		},
	{ "contains", array_contains	},
	{ NULL, NULL },
};

RedTypeObject RedType_Array =
{
	RedType_INIT("array", RedArrayObject, RedType_Object)

	.tp_len			= array_len,
	.tp_str			= array_repr,
	.tp_repr		= array_repr,

	.tp_init		= array_init,
	.tp_clear		= array_clear,
	.tp_release		= array_release,
	.tp_traverse	= array_traverse,

	.tp_delitem		= RedArray_DelItem,
	.tp_getitem		= RedArray_GetItem,
	.tp_setitem		= RedArray_SetItem,

	.tp_add			= RedArray_Concat,
	.tp_aug_add		= array_aug_add,

	.tp_methods		= array_methods,
};

RedObject *RedArray_Create(void)
{
	return RedObject_Create(&RedType_Array, NULL);
}

RedObject *RedArray_CreatePresized(int size)
{
	RedObject *result = RedObject_Create(&RedType_Array, RED_ARRAY_INIT_PRESIZE);

	if (!result)
		return NULL;

	if (!(RedArray_Items(result) = RedMem_Alloc(sizeof(RedObject *) * size)))
		return NULL;

	RedArray_NMods(result) = 0;
	RedArray_Length(result) = size;
	RedArray_Capacity(result) = size;
	memset(RedArray_Items(result), 0, sizeof(RedObject *) * size);
	return result;
}

static inline int cmp_object(RedObject *self, int nmods, int ia, RedObject *b, RedObject *cmp, char *ok)
{
	RedObject *result = NULL;
	RedObject *object = RedArray_Items(self)[ia];

	if (!cmp)
	{
		result = RedObject_Compare(object, b);
	}
	else
	{
		RedObject *args = RedTuple_FromObjects(2, object, b);

		result = RedObject_Invoke(cmp, args);
		Red_UNREF(args);
	}

	if (!result || !RedInt_Exact(result))
	{
		*ok = 0;
		RedException_ThrowFormat(&RedExc_ValueError, "Integer expected for comparison result");
		return 0;
	}

	if (!RedArray_CheckMods(self, nmods))
	{
		*ok = 0;
		return 0;
	}

	int64_t value = RedInt_Value(result);

	*ok = 1;
	Red_UNREF(result);
	return value < 0 ? -1 : value > 0 ? 1 : 0;
}

#define swap(a, b)	do { RedObject *_t = (a); (a) = (b); (b) = _t; } while (0)

static int qsort_partition(RedObject *self, int nmods, int a, int b, RedObject *cmp)
{
	char ok = 1;
	RedObject *pv = RedArray_Items(self)[a];

	swap(RedArray_Items(self)[a], RedArray_Items(self)[b]);

	for (int i = a; ok && i < b; i++)
		if (cmp_object(self, nmods, i, pv, cmp, &ok) < 0)
			swap(RedArray_Items(self)[a++], RedArray_Items(self)[i]);

	if (!ok)
		return -1;

	swap(RedArray_Items(self)[b], RedArray_Items(self)[a]);
	return a;
}

#undef swap

static char qsort_array(RedObject *self, int nmods, int a, int b, RedObject *cmp)
{
	if (a >= b)
		return 1;

	int pv = qsort_partition(self, nmods, a, b, cmp);
	return pv < 0 ? 0 : (qsort_array(self, nmods, a, pv - 1, cmp) && qsort_array(self, nmods, pv + 1, b, cmp));
}

char RedArray_Sort(RedObject *self, RedObject *cmp)
{
	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected as 'self' argument");
		return 0;
	}

	int nmods;
	char result;
	RedArray_WriteLock(self, nmods);

	if ((result = qsort_array(self, nmods, 0, RedArray_Length(self), cmp)))
		RedArray_Unlock(self);

	return result;
}

char RedArray_Clear(RedObject *self)
{
	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected as 'self' argument");
		return 0;
	}

	int nmods;
	RedArray_WriteLock(self, nmods);

	for (int i = 0; i < RedArray_Length(self); i++)
		Red_UNREF(RedArray_Items(self)[i]);

	RedArray_Length(self) = 0;
	RedArray_Unlock(self);
	return 1;
}

char RedArray_Append(RedObject *self, RedObject *item)
{
	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected as 'self' argument");
		return 0;
	}

	int nmods;
	RedArray_WriteLock(self, nmods);

	if (RedArray_Length(self) == RedArray_Capacity(self))
	{
		if (RedArray_Capacity(self))
			RedArray_Capacity(self) <<= 1;
		else
			RedArray_Capacity(self) = RED_ARRAY_INIT_SIZE;

		RedArray_Items(self) = RedMem_Realloc(RedArray_Items(self), sizeof(RedObject *) * RedArray_Capacity(self));
	}

	Red_REF(item);
	RedArray_Items(self)[RedArray_Length(self)++] = item;
	RedArray_Unlock(self);

	if (RedObject_GC_IsTracked(self))
		RedObject_GC_Insert(item);

	return 1;
}

char RedArray_Remove(RedObject *self, RedObject *item)
{
	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected as 'self' argument");
		return 0;
	}

	int nmods;
	RedArray_WriteLock(self, nmods);

	for (int i = 0; i < RedArray_Length(self); i++)
	{
		RedObject *eq = RedObject_Eq(item, RedArray_Items(self)[i]);

		if (!eq)
		{
			RedArray_Unlock(self);
			return 0;
		}

		char found = RedBool_IsTrue(eq);

		if (!RedArray_CheckMods(self, nmods))
		{
			Red_UNREF(eq);
			return 0;
		}

		if (found)
		{
			Red_UNREF(RedArray_Items(self)[i]);

			for (int j = i + 1; j < RedArray_Length(self); j++)
				RedArray_Items(self)[j - 1] = RedArray_Items(self)[j];

			RedArray_Unlock(self);
			return 1;
		}

		Red_UNREF(eq);
	}

	RedArray_Unlock(self);
	RedException_ThrowFormat(&RedExc_IndexError, "Item not found");
	return 0;
}

char RedArray_Extend(RedObject *self, RedObject *array)
{
	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected for 'self' argument");
		return 0;
	}

	int count = -1;
	int nmods1 = -1;
	int nmods2 = -1;

	if (RedTuple_Exact(array))
	{
		RedArray_WriteLock(self, nmods1);
		count = RedTuple_Length(array);
	}
	else if (RedArray_Exact(array))
	{
		RedArray_ReadLock(array, nmods2);
		RedArray_WriteLock(self, nmods1);
		count = RedArray_Length(array);
	}

	if (count < 0)
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' or 'tuple' expected for 'array' argument");
		return 0;
	}

	if (RedArray_Length(self) + count >= RedArray_Capacity(self))
	{
		if (!RedArray_Capacity(self))
			RedArray_Capacity(self) = count;
		else
			while (RedArray_Length(self) + count >= RedArray_Capacity(self))
				RedArray_Capacity(self) <<= 1;

		RedArray_Items(self) = RedMem_Realloc(RedArray_Items(self), sizeof(RedObject *) * RedArray_Capacity(self));
	}

	for (int i = 0; i < count; i++)
	{
		RedObject *item = NULL;

		if (RedArray_Exact(array))
			item = RedArray_Items(array)[i];
		else if (RedTuple_Exact(array))
			item = RedTuple_Items(array)[i];

		if (RedObject_GC_IsTracked(self))
			if (!RedObject_GC_IsTracked(array))
				RedObject_GC_Insert(item);

		Red_REF(item);
		RedArray_Items(self)[RedArray_Length(self)++] = item;
	}

	if (nmods2 >= 0)
		RedArray_Unlock(array);

	RedArray_Unlock(self);
	return 1;
}

char RedArray_Insert(RedObject *self, RedObject *item, int index)
{
	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected as 'self' argument");
		return 0;
	}

	int nmods;
	RedArray_WriteLock(self, nmods);

	if (index < 0 || index >= RedArray_Length(self))
	{
		RedArray_Unlock(self);
		RedException_ThrowFormat(&RedExc_IndexError, "Index %d out of range", index);
		return 0;
	}

	if (RedArray_Length(self) == RedArray_Capacity(self))
	{
		if (RedArray_Capacity(self))
			RedArray_Capacity(self) <<= 1;
		else
			RedArray_Capacity(self) = RED_ARRAY_INIT_SIZE;

		RedArray_Capacity(self) <<= 1;
		RedArray_Items(self) = RedMem_Realloc(RedArray_Items(self), sizeof(RedObject *) * RedArray_Capacity(self));
	}

	for (int i = RedArray_Length(self) - 1; i >= index ; i--)
		RedArray_Items(self)[i + 1] = RedArray_Items(self)[i];

	Red_REF(item);
	RedArray_Items(self)[index] = item;
	RedArray_Length(self)++;
	RedArray_Unlock(self);

	if (RedObject_GC_IsTracked(self))
		RedObject_GC_Insert(item);

	return 1;
}

RedObject *RedArray_Pop(RedObject *self)
{
	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected as 'self' argument");
		return NULL;
	}

	int nmods;
	RedArray_WriteLock(self, nmods);

	if (!RedArray_Length(self))
	{
		RedArray_Unlock(self);
		RedException_ThrowFormat(&RedExc_IndexError, "Try pop an empty array");
		return NULL;
	}

	RedObject *item = RedArray_Items(self)[--RedArray_Length(self)];

	RedArray_Items(self)[RedArray_Length(self)] = NULL;
	RedArray_Unlock(self);
	return item;
}

RedObject *RedArray_Copy(RedObject *self)
{
	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected as 'self' argument");
		return NULL;
	}

	int nmods;
	RedObject *result = RedArray_CreatePresized(RedArray_Length(self));

	RedArray_ReadLock(self, nmods);

	for (int i = 0; i < RedArray_Length(self); i++)
	{
		Red_REF(RedArray_Items(self)[i]);
		RedArray_Items(result)[i] = RedArray_Items(self)[i];
	}

	RedArray_Unlock(self);

	if (RedObject_GC_IsTracked(self))
		RedObject_GC_Insert(result);

	return result;
}

RedObject *RedArray_Concat(RedObject *self, RedObject *array)
{
	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected as 'self' argument");
		return NULL;
	}

	int count = -1;
	int nmods1 = -1;
	int nmods2 = -1;

	if (RedTuple_Exact(array))
	{
		RedArray_WriteLock(self, nmods1);
		count = RedTuple_Length(array);
	}
	else if (RedArray_Exact(array))
	{
		RedArray_ReadLock(array, nmods2);
		RedArray_WriteLock(self, nmods1);
		count = RedArray_Length(array);
	}

	if (count < 0)
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' or 'tuple' expected for 'array' argument");
		return 0;
	}

	RedObject *result = RedArray_CreatePresized(RedArray_Length(self) + RedArray_Length(array));

	for (int i = 0; i < RedArray_Length(self); i++)
	{
		if (RedObject_GC_IsTracked(array))
			if (!RedObject_GC_IsTracked(self))
				RedObject_GC_Insert(RedArray_Items(self)[i]);

		Red_REF(RedArray_Items(self)[i]);
		RedArray_Items(result)[i] = RedArray_Items(self)[i];
	}

	for (int i = 0; i < count; i++)
	{
		RedObject *item = NULL;

		if (RedArray_Exact(array))
			item = RedArray_Items(array)[i];
		else if (RedTuple_Exact(array))
			item = RedTuple_Items(array)[i];

		if (RedObject_GC_IsTracked(self))
			if (!RedObject_GC_IsTracked(array))
				RedObject_GC_Insert(item);

		Red_REF(item);
		RedArray_Items(result)[RedArray_Length(self) + i] = item;
	}

	if (nmods2 >= 0)
		RedArray_Unlock(array);

	RedArray_Unlock(self);

	if (RedObject_GC_IsTracked(self) || RedObject_GC_IsTracked(array))
		RedObject_GC_Insert(result);

	return result;
}

RedObject *RedArray_DelItem(RedObject *self, RedObject *index)
{
	if (!RedInt_Exact(index))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'int' expected as 'index' argument");
		return NULL;
	}

	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected as 'self' argument");
		return NULL;
	}

	int pos = (int)RedInt_Value(index);
	int nmods = 0;
	RedArray_WriteLock(self, nmods);

	if (pos < 0 || pos >= RedArray_Length(self))
	{
		RedArray_Unlock(self);
		RedException_ThrowFormat(&RedExc_IndexError, "Index %d out of range", pos);
		return NULL;
	}

	Red_UNREF(RedArray_Items(self)[pos]);

	for (int i = pos + 1; i < RedArray_Length(self); i++)
		RedArray_Items(self)[i - 1] = RedArray_Items(self)[i];

	RedArray_Unlock(self);
	Red_Return_Null();
}

RedObject *RedArray_GetItem(RedObject *self, RedObject *index)
{
	if (!RedInt_Exact(index))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'int' expected as 'index' argument");
		return NULL;
	}

	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected as 'self' argument");
		return NULL;
	}

	int pos = (int)RedInt_Value(index);
	int nmods = 0;
	RedArray_ReadLock(self, nmods);

	if (pos < 0 || pos >= RedArray_Length(self))
	{
		RedException_ThrowFormat(&RedExc_IndexError, "Index %d out of range", pos);
		return NULL;
	}

	RedObject *item = RedArray_Items(self)[pos];

	Red_REF(item);
	RedArray_Unlock(self);
	return item;
}

RedObject *RedArray_SetItem(RedObject *self, RedObject *index, RedObject *item)
{
	if (!RedInt_Exact(index))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'int' expected as 'index' argument");
		return 0;
	}

	if (!RedArray_Exact(self))
	{
		RedException_ThrowFormat(&RedExc_TypeError, "'array' expected as 'self' argument");
		return 0;
	}

	int pos = (int)RedInt_Value(index);
	int nmods = 0;
	RedArray_WriteLock(self, nmods);

	if (pos < 0 || pos >= RedArray_Length(self))
	{
		RedArray_Unlock(self);
		RedException_ThrowFormat(&RedExc_IndexError, "Index %d out of range", pos);
		return NULL;
	}

	Red_REF(item);
	Red_UNREF(RedArray_Items(self)[pos]);
	RedArray_Items(self)[pos] = item;
	RedArray_Unlock(self);

	if (RedObject_GC_IsTracked(self))
		RedObject_GC_Insert(item);

	Red_Return_Null();
}
